/**
 * clase que implementa los resortes, extiende de PhysicsElement.
 * @version 2
 */
public class Spring extends PhysicsElement  {
   private static int id=0;  // Spring identification
   protected final float restLength;
   private final float stiffness;
   protected AttachableElement a_end, b_end;

   /**
    * Construstor de los resortes.
    */
   private Spring(){   // nobody can create a block without state
      super(id++);
      stiffness = restLength = 0;
   }
   /**
    * Constructor de los resortes con parámetros iniciales.
    * @param restLength largo del resorte.
    * @param stiffness constante del resorte.
    */
   public Spring(float restLength, float stiffness){
      super(id++);
      this.restLength = restLength;
      this.stiffness = stiffness;
      a_end = b_end = null;
   }
   /**
    * Función que acopla un elemento al extremo A.
    * @param ae elemento acoplable.
    */
   public void attachAend (AttachableElement ae) {
      a_end = ae;
      ae.attachSpring(this);
   }
   /**
    * Función que desacopla un elemento del extremo A.
    * @param ae elemento a desacoplar.
    */
   public void detachAend(){
      if (a_end == null) return;
      a_end.detachSpring(this);
      a_end=null;
   }
   /**
    * Función que acopla un elemento al extremo B.
    * @param ae elemento acoplable.
    */
   public void attachBend (AttachableElement ae) {
      b_end = ae;
      ae.attachSpring(this);
   }
   /**
    * Función que desacopla un elemento del extremo B.
    * @param ae elemento desacoplable.
    */
   public void detachBend(){
      if (b_end == null) return;
      b_end.detachSpring(this);
      b_end = null;
   }
   /**
    * Función que retorna el vector de posición con el extremo A.
    * @return vector de posición del extremo A.
    */
   public Vector2D getAendPosition() {
      if (a_end != null)
         return a_end.getPosition();
      if (b_end != null)
         return b_end.getPosition().minus(new Vector2D(0,restLength));
      return new Vector2D();
   }
   /**
    * Función que retorna el vector de posición con el extremo B.
    * @return vector de posición del extremo B.
    */
   public Vector2D getBendPosition() {
      if (b_end != null)
         return b_end.getPosition();
      if (a_end != null)
         return a_end.getPosition().plus(new Vector2D(0,restLength));
      return new Vector2D(0,restLength);
   }
   /**
    * Función para obtener el vector que describe el largo del resorte.
    * @return vector.
    */
   protected Vector2D getVector() {
      return getBendPosition().minus(getAendPosition());
   }
   /**
    * Función que calcula la fuerza neta aplicada por el resorte.
    * @param ae elemento acoplado al extremo.
    * @return fuerza neta si es el extremo A y la fuerza en sentido inverso si
    * es el extremo B.
    */
   public Vector2D getForce(AttachableElement ae) {
      Vector2D force = new Vector2D();
      if ((a_end == null) || (b_end == null))
         return force;
      if ((ae != a_end) && (ae != b_end))
         return force;
      Vector2D springVector = getVector();
      double stretch = springVector.module() - restLength;
      force = springVector.unitary().times(stretch*stiffness);
      if (ae == a_end) return force;
      return force.times(-1.0);
   }
   /**
    * Función que entrega la descripción del resorte, junto a la posición de
    * sus extremos.
    * @return string con la identificación y posiciones de los extremos.
    */
   public String getDescription() {
      return "Spring_"+ getId()+": "+ Vector2D.getDescription()+" ,"+ Vector2D.getDescription();
   }
   /**
    * Función que entrega la la posición de los extremos del resorte en un
    * string.
    * @return string con las posciones de los extremos del elástico.
    */
   public String getState() {
      String s = getAendPosition() + "," + getBendPosition();
      return s;
   }
}
